import 'package:equatable/equatable.dart';
import 'package:im_app/im/util/DbUtil.dart';

import '../../dao/ImUserDao.dart';
import '../../pojo/Enums.dart';
import '../../pojo/ImPojo.dart';
import '../../util/LoggerUtil.dart';
import 'BaseBloc.dart';

final class SelectUsersCubit extends BaseBloc<SelectUsersEvent, SelectUsersState> {

  final _logger = createLogger();

  SelectUsersCubit(Set<String>? existedUserIdSet) : super(SelectUsersState.loading(existedUserIdSet?? new Set<String>())) {
    on<SelectUsersAddUserEvent>((event, emit) {
      _addUser(state, event.user);
    });
    on<SelectUsersRemoveUserEvent>((event, emit){
      _removeUser(state, event.user);
    });
    on<SelectUsersSearchEvent>((event, emit){
      _search(state, event.search);
    });
    on<SelectUsersResetEvent>((event, emit){
      _reset(state);
    });
    on<SelectUsersCancelEvent>((event, emit){
      _cancel(state);
    });
  }

  _addUser(SelectUsersState state, ImUser user) {
    Map<String, ImUser> selectUserMap = copyMap(state.selectedUserMap);
    List<ImUser> selectedUsers = copyList(state.selectedUsers);
    selectUserMap[user.userId] = user;
    selectedUsers.add(user);

    SelectUsersState newState = state.copy(selectedUsers: selectedUsers, selectedUserMap: selectUserMap);
    emit(newState);
  }

  _removeUser(SelectUsersState state, ImUser user) {
    Map<String, ImUser> selectUserMap = copyMap(state.selectedUserMap);
    selectUserMap.remove(user.userId);

    List<ImUser> selectedUsers = copyList(state.selectedUsers);
    selectedUsers.remove(user);
    SelectUsersState newState = state.copy(selectedUsers: selectedUsers, selectedUserMap: selectUserMap);
    emit(newState);
  }

  _search(SelectUsersState state, String search) {
    List<ImUser> showUsers = state.allUsers.where((u)=>u.userName!.contains(search) || u.userId!.contains(search))
        .toList();
    var newState = state.copy(showUsers: showUsers, search: search);
    emit(newState);
  }

  _reset(SelectUsersState state) async {
    var newState = SelectUsersState.success(state.existedUserIdSet, state.allUsers, state.letters, state.letterSizeMap);
    emit(newState);
  }

  _cancel(SelectUsersState state) {
    SelectUsersState newState = state.copy(selectedUsers: <ImUser>[], selectedUserMap: <String, ImUser>{});
    emit(newState);
  }

  Future<void> loadData(Set<String> existedUserSet) async {
    _logger.i('SelectUsersCubit loadData!!!!!!!!!!', time: DateTime.now());
    var userDao = ImUserDao();
    List<ImUser> users = await userDao.getUsers(10000);
    List<String> letters = _calcLetter(users);
    SelectUsersState newState = SelectUsersState.success(existedUserSet, users, letters, _calcLetters(users));
    emit(newState);
  }

  List<String> _calcLetter(List<ImUser> users) {
    Set<String> letterSet = users.map((u)=>(u.firstLetter??'#').toUpperCase()).toSet();
    List<String> letters = letterSet.toList();
    letters.sort();
    return letters;
  }

  Map<String, int> _calcLetters(List<ImUser> users) {
    Map<String, int> map = {};
    for (var user in users) {
      var key = (user.firstLetter??'#').toUpperCase();
      if (map.containsKey(key)) {
        int value = map[key]!;
        map[key] = value + 1;
      } else {
        map[key] = 1;
      }
    }
    return map;
  }
}

final class SelectUsersState extends Equatable {
  final Set<String> existedUserIdSet;
  final LoadingState state;
  final List<ImUser> selectedUsers;
  final Map<String, ImUser> selectedUserMap;
  final List<ImUser> allUsers;
  final List<ImUser> filterUsers;
  final List<String> letters;
  final Map<String, int> letterSizeMap;
  final String search;

  const SelectUsersState._({
    required this.existedUserIdSet,
    this.state = LoadingState.loading,
    this.allUsers = const <ImUser>[],
    this.filterUsers = const <ImUser>[],
    this.selectedUsers = const <ImUser>[],
    this.selectedUserMap = const <String, ImUser>{},
    this.letters = const <String>[],
    this.letterSizeMap = const {},
    this.search = '',
  });

  SelectUsersState copy({Set<String>? existedUserIdSet, List<ImUser>? allUsers, List<ImUser>? selectedUsers,
    Map<String, ImUser>? selectedUserMap, List<ImUser>? showUsers, List<String>? letters, Map<String, int>? letterSizeMap,
    String? search, String? forwardRouter, ImSession? forwardSession}) {
    return SelectUsersState._(
      existedUserIdSet: existedUserIdSet ?? this.existedUserIdSet,
      allUsers: allUsers ?? this.allUsers,
      selectedUsers: selectedUsers ?? this.selectedUsers,
      selectedUserMap: selectedUserMap ?? this.selectedUserMap,
      filterUsers: showUsers ?? this.filterUsers,
      letters: letters ?? this.letters,
      letterSizeMap: letterSizeMap ?? this.letterSizeMap,
      search: search ?? this.search,
      state: this.state,
    );
  }

  const SelectUsersState.loading(Set<String> existedUserIdSet) : this._(existedUserIdSet: existedUserIdSet);

  const SelectUsersState.success(Set<String> existedUserIdSet, List<ImUser> allUsers, List<String> letters,
  Map<String, int> letterSizeMap)
      : this._(state: LoadingState.success, existedUserIdSet: existedUserIdSet,
      allUsers: allUsers, filterUsers: allUsers, letters: letters, letterSizeMap: letterSizeMap);

  @override
  List<Object?> get props => [state, filterUsers, selectedUsers, letters, search];
}



abstract class SelectUsersEvent {}
final class SelectUsersAddUserEvent extends SelectUsersEvent {
  final ImUser user;
  SelectUsersAddUserEvent(this.user);
}
final class SelectUsersRemoveUserEvent extends SelectUsersEvent {
  final ImUser user;
  SelectUsersRemoveUserEvent(this.user);
}

final class SelectUsersSearchEvent extends SelectUsersEvent {
  final String search;
  SelectUsersSearchEvent(this.search);
}
final class SelectUsersResetEvent extends SelectUsersEvent {
  SelectUsersResetEvent();
}
final class SelectUsersCancelEvent extends SelectUsersEvent {
  SelectUsersCancelEvent();
}